package com.supermap.ar.unity.player;

import android.app.Activity;
import android.content.Intent;
import android.content.res.Configuration;
import android.os.Bundle;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.widget.RelativeLayout;

import com.google.are.sceneform.Camera;
import com.google.are.sceneform.math.Quaternion;
import com.google.are.sceneform.math.Vector3;
import com.supermap.ar.areffect.EffectView;
import com.supermap.ar.unity.exception.NotSupportException;
import com.supermap.ar.unity.slam.CameraPoseData;
import com.supermap.ar.unity.slam.CameraPoseListener;
import com.supermap.ar.unity.slam.SlamObject;
import com.unity3d.player.IUnityPlayerLifecycleEvents;
import com.unity3d.player.MultiWindowSupport;

/**
 * unity场景作为背景的Activity
 * <p>与Unity组件SuperMap_IMB_System结合使用</p>
 * @author tanyx 2022/3/3
 * @version 1.0
 * <br/>SampleCode:<br/>
 * <code>
 *
 * </code>
 **/
public abstract class BackUnityActivity extends Activity implements IUnityPlayerLifecycleEvents,CameraPoseListener
{
    private EffectView mEffectView;
    private View mForegroundView;
    protected BackUnityPlayer mUnityPlayer; // don't change the name of this variable; referenced from native code

    /**
     * 创建前景视图
     * <p>在该方法类调用{@link BackUnityActivity#setForegroundView(View)}</p>
     * @param savedInstanceState
     */
    public abstract void onCreateForegroundView(Bundle savedInstanceState);

    /**
     *
     */
    private Camera camera;
    @Override
    public void onStartSlam() {
        camera = getEffectView().getSceneView().getScene().getCamera();
    }

    /**
     *
     */
    @Override
    public void onDestroySlam() {
        camera = null;
    }

    /**
     *
     * @param cameraPoseData
     */
    @Override
    public void updateCameraPose(CameraPoseData cameraPoseData) {
        if (camera == null)return;
        camera.setFarClipPlane(cameraPoseData.getFar());
        camera.setNearClipPlane(cameraPoseData.getNear());
        camera.setVerticalFovDegrees(cameraPoseData.getFov());
        camera.setWorldPosition(
                new Vector3(cameraPoseData.getPositionX(),
                        cameraPoseData.getPositionY(),
                        -cameraPoseData.getPositionZ()));
        Vector3 eulerAngles = new Vector3(
                /*X:ROLL*/-cameraPoseData.getRotationX(),
                /*y:PITCH*/-cameraPoseData.getRotationY(),
                /*Z:YAW*/cameraPoseData.getRotationZ()
        );
        camera.setWorldRotation(Quaternion.eulerAngles(eulerAngles));
    }


    // Override this in your custom UnityPlayerActivity to tweak the command line arguments passed to the Unity Android Player
    // The command line arguments are passed as a string, separated by spaces
    // UnityPlayerActivity calls this from 'onCreate'
    // Supported: -force-gles20, -force-gles30, -force-gles31, -force-gles31aep, -force-gles32, -force-gles, -force-vulkan
    // See https://docs.unity3d.com/Manual/CommandLineArguments.html
    // @param cmdLine the current command line arguments, may be null
    // @return the modified command line string or null
    protected String updateUnityCommandLineArguments(String cmdLine)
    {
        return cmdLine;
    }

    public EffectView getEffectView() {
        return mEffectView;
    }

    // Setup activity layout
    @Override protected void onCreate(Bundle savedInstanceState)
    {
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        super.onCreate(savedInstanceState);

        String cmdLine = updateUnityCommandLineArguments(getIntent().getStringExtra("unity"));
        getIntent().putExtra("unity", cmdLine);

        mUnityPlayer = new BackUnityPlayer(this, this);
        super.setContentView(mUnityPlayer);
        mUnityPlayer.requestFocus();

        //Custom: add EffectView
        mEffectView = new EffectView(this);
        mEffectView.setLayoutParams(new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
        mEffectView.getSceneView().setTransparent(true);
        mUnityPlayer.addView(mEffectView);
        onCreateForegroundView(savedInstanceState);
        if (this.mForegroundView!=null){
            mUnityPlayer.addView(mForegroundView);
        }
    }

    /**
     * 获取前景视图
     * @return
     */
    public View getForegroundView() {
        return mForegroundView;
    }

    public void setForegroundView(View view){
        this.mForegroundView = view;
    }
    public void setForegroundView(View view, ViewGroup.LayoutParams params){
        view.setLayoutParams(params);
        this.mForegroundView = view;
    }
    public void setForegroundView(int layoutResID){
        View inflate = LayoutInflater.from(this).inflate(layoutResID, null);
        inflate.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
        this.mForegroundView = inflate;
    }

    // When Unity player unloaded move task to background
    @Override public void onUnityPlayerUnloaded() {
        moveTaskToBack(true);
    }

    // Callback before Unity player process is killed
    @Override public void onUnityPlayerQuitted() {
    }

    @Override protected void onNewIntent(Intent intent)
    {
        // To support deep linking, we need to make sure that the client can get access to
        // the last sent intent. The clients access this through a JNI api that allows them
        // to get the intent set on launch. To update that after launch we have to manually
        // replace the intent with the one caught here.
        setIntent(intent);
        mUnityPlayer.newIntent(intent);
    }

    // Quit Unity
    @Override protected void onDestroy ()
    {
        if (mEffectView!=null){
            SlamObject.getInstance().setCameraPoseListener(null);
            mEffectView.onDestroy();
        }
        mUnityPlayer.destroy();
        super.onDestroy();
    }

    // If the activity is in multi window mode or resizing the activity is allowed we will use
    // onStart/onStop (the visibility callbacks) to determine when to pause/resume.
    // Otherwise it will be done in onPause/onResume as Unity has done historically to preserve
    // existing behavior.
    @Override protected void onStop()
    {
        super.onStop();

        if (!MultiWindowSupport.getAllowResizableWindow(this))
            return;

        mUnityPlayer.pause();
        if (mEffectView!=null){
            SlamObject.getInstance().setCameraPoseListener(null);
            mEffectView.onPause();
        }

    }

    @Override protected void onStart()
    {
        super.onStart();

        if (!MultiWindowSupport.getAllowResizableWindow(this))
            return;

        mUnityPlayer.resume();
        if (mEffectView!=null){
            mEffectView.onResume();
            SlamObject.getInstance().setCameraPoseListener(this);
        }

    }

    // Pause Unity
    @Override protected void onPause()
    {
        super.onPause();

        if (MultiWindowSupport.getAllowResizableWindow(this))
            return;

        mUnityPlayer.pause();
        if (mEffectView!=null){
            SlamObject.getInstance().setCameraPoseListener(null);
            mEffectView.onPause();
        }

    }

    // Resume Unity
    @Override protected void onResume()
    {
        super.onResume();

        if (MultiWindowSupport.getAllowResizableWindow(this))
            return;

        mUnityPlayer.resume();
        if (mEffectView!=null){
            mEffectView.onResume();
            SlamObject.getInstance().setCameraPoseListener(this);
        }
    }

    // Low Memory Unity
    @Override public void onLowMemory()
    {
        super.onLowMemory();
        mUnityPlayer.lowMemory();
    }

    // Trim Memory Unity
    @Override public void onTrimMemory(int level)
    {
        super.onTrimMemory(level);
        if (level == TRIM_MEMORY_RUNNING_CRITICAL)
        {
            mUnityPlayer.lowMemory();
        }
    }

    // This ensures the layout will be correct.
    @Override public void onConfigurationChanged(Configuration newConfig)
    {
        super.onConfigurationChanged(newConfig);
        mUnityPlayer.configurationChanged(newConfig);
    }

    // Notify Unity of the focus change.
    @Override public void onWindowFocusChanged(boolean hasFocus)
    {
        super.onWindowFocusChanged(hasFocus);
        mUnityPlayer.windowFocusChanged(hasFocus);
    }

    // For some reason the multiple keyevent type is not supported by the ndk.
    // Force event injection by overriding dispatchKeyEvent().
    @Override public boolean dispatchKeyEvent(KeyEvent event)
    {
        if (event.getAction() == KeyEvent.ACTION_MULTIPLE)
            return mUnityPlayer.injectEvent(event);
        return super.dispatchKeyEvent(event);
    }

    // Pass any events not handled by (unfocused) views straight to UnityPlayer
    @Override public boolean onKeyUp(int keyCode, KeyEvent event)     { return mUnityPlayer.injectEvent(event); }
    @Override public boolean onKeyDown(int keyCode, KeyEvent event)   { return mUnityPlayer.injectEvent(event); }
    @Override public boolean onTouchEvent(MotionEvent event)          { return mUnityPlayer.injectEvent(event); }
    /*API12*/ public boolean onGenericMotionEvent(MotionEvent event)  { return mUnityPlayer.injectEvent(event); }

    @Override
    public void setContentView(int layoutResID) {
        throw new NotSupportException("Not Support. setForegroundView() instead of setContentView()");
    }

    @Override
    public void setContentView(View view) {
        throw new NotSupportException("Not Support. setForegroundView() instead of setContentView()");
    }

    @Override
    public void setContentView(View view, ViewGroup.LayoutParams params) {
        throw new NotSupportException("Not Support. setForegroundView() instead of setContentView()");
    }
}
